#include "Program.h"
#include "Util/Util.h"
#include "Common.h"

OpenCVTools::OpenCVTools()
{
    _MatchTemplateMatVec.clear();
    _MatchTemplateMatConditionVec.clear();
    _MatchTemplateMatMouseClickMap.clear();
    OpenCVToolBindHwnd = 0;
    CurState = 0;
    ReposeTime = 0;
}

OpenCVTools::~OpenCVTools()
{

}

Mat OpenCVTools::hwnd2mat(HWND hwnd) {

	HDC hwindowDC, hwindowCompatibleDC;

	int height, width, srcheight, srcwidth;
	HBITMAP hbwindow;
	Mat src;
	BITMAPINFOHEADER  bi;

	hwindowDC = GetDC(hwnd);
	hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
	SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);

	RECT windowsize;    // get the height and width of the screen
	GetClientRect(hwnd, &windowsize);

	srcheight = windowsize.bottom;
	srcwidth = windowsize.right;
	height = windowsize.bottom;  //change this to whatever size you want to resize to
	width = windowsize.right;

	src.create(height, width, CV_8UC4);

	// create a bitmap
	hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = width;
	bi.biHeight = -height;  //this is the line that makes it draw upside down or not
	bi.biPlanes = 1;
	bi.biBitCount = 32;
	bi.biCompression = BI_RGB;
	bi.biSizeImage = 0;
	bi.biXPelsPerMeter = 0;
	bi.biYPelsPerMeter = 0;
	bi.biClrUsed = 0;
	bi.biClrImportant = 0;

	// use the previously created device context with the bitmap
	SelectObject(hwindowCompatibleDC, hbwindow);
	// copy from the window device context to the bitmap device context
	StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 0, 0, srcwidth, srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
	GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);  //copy from hwindowCompatibleDC to hbwindow

	// avoid memory leak
	DeleteObject(hbwindow); DeleteDC(hwindowCompatibleDC); ReleaseDC(hwnd, hwindowDC);

	return src;
}

HWND OpenCVTools::GetHwndByPid(DWORD dwProcessID)
{
	HWND h = GetTopWindow(0);
	HWND retHwnd = NULL;
	while (h)
	{
		DWORD pid = 0;
		DWORD dwTheardId = GetWindowThreadProcessId(h, &pid);
		if (dwTheardId != 0)
		{
			if (pid == dwProcessID && GetParent(h) == NULL && ::IsWindowVisible(h))
			{
				retHwnd = h;    //会有多个相等值
				//    /*
				//char buf[MAX_PATH] = { 0 };
				//sprintf_s(buf, "%0x", h);
				//MessageBox(NULL, buf, "提示", MB_OK);
				//    */
				break;
			}
		}
		h = GetNextWindow(h, GW_HWNDNEXT);
	}

    OpenCVTools::instance()->OpenCVToolBindHwnd = retHwnd;

	return retHwnd;
}

int OpenCVTools::MatchTemplateFromState(int state, Mat NowImg)
{
    int ret = -1;

    for (auto itr : _MatchTemplateMatVec)
    {
        if (itr.state == state)
        {
            Mat result;

            if (instance()->MatchTemplateFromTmp(NowImg, itr.matData, result, itr.poi))
            {
                ret = itr.conditionState;
            }
            else
            {
                // 只要有一项不符合就返回假
                ret = -1;
            }
        }
    }

    return ret;
}

int OpenCVTools::MatchTemplateConditionFromState(int state, Mat NowImg)
{
    int ret = -1;

    for (auto itr : _MatchTemplateMatConditionVec)
    {
        if (itr.state == state)
        {
            Mat result;

            if (instance()->MatchTemplateFromTmp(NowImg, itr.matData, result, itr.poi))
            {
                ret = itr.conditionState;

                // 匹配成功立即结束返回
                break;
            }
        }
    }

    return ret;
}

bool OpenCVTools::MouseClickFromMatchTemplateState(int state, Mat NowImg)
{
    auto itr = _MatchTemplateMatMouseClickMap.find(state);

    if (itr == _MatchTemplateMatMouseClickMap.end())
    {
        // 当前状态无需点击，直接返回真
        cout << "状态:" << state << " 未注册点击模板,跳过。" << endl;
        return true;
    }

    Mat result;

    if (instance()->MatchTemplateFromTmp(NowImg, itr->second.matData, result, itr->second.poi))
    {
        if (result.channels() == 1)
        {
            float max_resemble = 0.0f;
            Point matchLoc;
            int now_cols = itr->second.matData.cols;
            int now_rows = itr->second.matData.rows;

            MatIterator_<float> it_begin, it_end;
            for (it_begin = result.begin<float>(), it_end = result.end<float>(); it_begin != it_end; it_begin++)
            {
                // 相似度
                if (*it_begin >= itr->second.poi)
                {
                    if (*it_begin > max_resemble)
                    {
                        max_resemble = *it_begin;
                        matchLoc = it_begin.pos();
                    }

                    //rectangle(src, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(255, 0, 0), 2, 8, 0);

                }
            }
            rectangle(NowImg, matchLoc, Point(matchLoc.x + now_cols, matchLoc.y + now_rows), Scalar(255, 0, 0), 2, 8, 0);

            int click_x = matchLoc.x + irand(now_cols / 3, now_cols - (now_cols / 3));
            int click_y = matchLoc.y + irand(now_rows / 3, now_rows - (now_rows / 3));

            // 经过测试，虽然屏幕缩放比不对，但是仅仅只是显示的坐标错误。实际上模拟的鼠标点击坐标是对的。。。搞不懂了。。。
            //click_x *= OpenCvMachine->GetMonitorScale();
            //click_y *= OpenCvMachine->GetMonitorScale();

            instance()->PostMouseClickMessage(OpenCVTools::instance()->OpenCVToolBindHwnd, click_x, click_y);

            cout << GetNowTimeStr() << "最大匹配度: " << max_resemble << " 坐标: " << matchLoc.x + now_cols << "," << matchLoc.y + now_rows << " 点击: " << click_x << "," << click_y << endl;
            return true;
        }
    }

    return false;
}

int OpenCVTools::MatchTemplateFromTmp(Mat NowImg ,Mat tmpImg, Mat & result, float poi)
{
	int srcW, srcH, templatW, templatH;
	srcW = NowImg.cols;
	srcH = NowImg.rows;
	templatW = tmpImg.cols;
	templatH = tmpImg.rows;
	if (srcW < templatW || srcH < templatH)
	{
		cout << "模板不能比原图小" << endl;
		return 0;
	}

	int resultW = srcW - templatW + 1;
	int resultH = srcH - templatH + 1;
	result.create(Size(resultW, resultH), CV_32FC1);
	matchTemplate(NowImg, tmpImg, result, TM_CCOEFF_NORMED);

	//cout << "depth:" << result.depth() << endl;
	//cout << "type:" << result.type() << endl;
	//cout << "channels:" << result.channels() << endl;
	//cout << "total:" << result.total() << endl;

	Point matchLoc;
	int count = 0;

	if (result.channels() == 1)
	{
		MatIterator_<float> it_begin, it_end;

		for (it_begin = result.begin<float>(), it_end = result.end<float>(); it_begin != it_end; it_begin++)
		{
			// 相似度
			if (*it_begin >= poi)
			{
				count++;
				//matchLoc = it_begin.pos();

				//rectangle(NowImg, matchLoc, Point(matchLoc.x + tmpImg.cols, matchLoc.y + tmpImg.rows), Scalar(255, 0, 0), 2, 8, 0);
			}
		}
		//cout << "命中数量: " << count << endl;
	}

	return count;
}

void OpenCVTools::RegisterMatchMatTemplate(int state, Mat matTemplate, float poi, int resState)
{
    if (!matTemplate.empty())
    {
        MatTemplate matTmp;
        matTmp.state = state;
        matTmp.matData = matTemplate;
        matTmp.poi = poi;
        matTmp.conditionState = resState;

        _MatchTemplateMatVec.push_back(matTmp);
    }
}

void OpenCVTools::RegisterConditionMatchMatTemplate(int state, Mat matTemplate, float poi, int resState)
{
    if (!matTemplate.empty())
    {
        MatTemplate matTmp;
        matTmp.state = state;
        matTmp.matData = matTemplate;
        matTmp.poi = poi;
        matTmp.conditionState = resState;

        _MatchTemplateMatConditionVec.push_back(matTmp);
    }
}

void OpenCVTools::RegisterMatchMatMouseClick(int state, Mat matTemplate, float poi)
{
    if (!matTemplate.empty())
    {
        MatTemplate matTmp;
        matTmp.state = state;
        matTmp.matData = matTemplate;
        matTmp.poi = poi;
        matTmp.conditionState = -1;

        _MatchTemplateMatMouseClickMap.emplace(state, matTmp);
    }
}

double OpenCVTools::GetMonitorScale()
{
	// 获取窗口当前显示的监视器
	// 使用桌面的句柄.
	HWND hWnd = GetDesktopWindow();
	HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);

	// 获取监视器逻辑宽度与高度
	MONITORINFOEX miex;
	miex.cbSize = sizeof(miex);
	GetMonitorInfo(hMonitor, &miex);
	int cxLogical = (miex.rcMonitor.right - miex.rcMonitor.left);
	int cyLogical = (miex.rcMonitor.bottom - miex.rcMonitor.top);

	// 获取监视器物理宽度与高度
	DEVMODE dm;
	dm.dmSize = sizeof(dm);
	dm.dmDriverExtra = 0;
	EnumDisplaySettings(miex.szDevice, ENUM_CURRENT_SETTINGS, &dm);
	int cxPhysical = dm.dmPelsWidth;
	int cyPhysical = dm.dmPelsHeight;
	// 缩放比例计算  实际上使用任何一个即可
	double horzScale = ((double)cxPhysical / (double)cxLogical);
	double vertScale = ((double)cyPhysical / (double)cyLogical);
	//printf("%lf  %lf\n", horzScale, vertScale);
	assert(horzScale == vertScale); // 宽或高这个缩放值应该是相等的
	return horzScale;
}

// 屏幕坐标相对客户区坐标转换
//ScreenToClient()
//ClientToScreen()
// 计算鼠标移动绝对位移相对屏幕坐标系的比例
//int OpenCVTools::GetMouseCoor2ScreenRatio()
//{
//	CPoint ptMouse, ptMouseOld;
//	GetPhysicalCursorPos(&ptMouseOld);
//	MoveMouse(0, 000);
//	MoveMouse(1000, 1000);
//	Sleep(10);
//	BOOL bRet = ::GetPhysicalCursorPos(&ptMouse);
//	if (TRUE == bRet)
//	{
//		m_RatioX = (double)1000 / ptMouse.x;//1000 是鼠标移动的绝对位移
//		m_RatioY = (double)1000 / ptMouse.y;//1000 是鼠标移动的绝对位移
//		MoveMouse(ptMouseOld.x * m_RatioX, ptMouseOld.y * m_RatioY);//归位	
//		return 0;
//	}
//	else
//	{
//		bRet = GetLastError();
//	}
//
//	return -1;
//}

// 另一种移动鼠标指针方法
// POINT p;
// ::GetCursorPos(&p);
// Mouse_Move(p.x + 50, p.y + 50);
void OpenCVTools::Mouse_Move(int x, int y)
{
	double fScreenWidth = ::GetSystemMetrics(SM_CXSCREEN) - 1;
	double fScreenHeight = ::GetSystemMetrics(SM_CYSCREEN) - 1;
	double fx = x * (65535.0f / fScreenWidth);
	double fy = y * (65535.0f / fScreenHeight);
	mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, fx, fy, 0, 0);
}

// 移动鼠标到绝对位置
int OpenCVTools::MoveMouse(unsigned int dx, unsigned int dy)
{
	INPUT inputs[1] = {};
	ZeroMemory(inputs, sizeof(inputs));

	inputs[0].type = INPUT_MOUSE;
	inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
	inputs[0].mi.dx = dx;
	inputs[0].mi.dy = dy;

	int nSize = ARRAYSIZE(inputs);
	UINT uSent;
	uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));

	if (uSent != ARRAYSIZE(inputs))
	{
		//OutputString(L"SendInput failed: 0x%x\n", HRESULT_FROM_WIN32(GetLastError()));
	}
	return 0;
}

// 发送鼠标左键单击消息
int OpenCVTools::MouseClick(int nMode)
{
	INPUT inputs[1] = {};
	ZeroMemory(inputs, sizeof(inputs));

	inputs[0].type = INPUT_MOUSE;
	inputs[0].mi.dwFlags = nMode | MOUSEEVENTF_ABSOLUTE;
	inputs[0].mi.dx = 0;
	inputs[0].mi.dy = 0;

	int nSize = ARRAYSIZE(inputs);
	UINT uSent;
	uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));

	if (uSent != ARRAYSIZE(inputs))
	{
		//OutputString(L"SendInput failed: 0x%x\n", HRESULT_FROM_WIN32(GetLastError()));
	}

	return 0;
}

void OpenCVTools::PostMouseClickMessage(HWND hwnd, int x, int y)
{
	PostMessage(hwnd, WM_MOUSEHOVER, 0, MAKELPARAM(0, 0));
	PostMessage(hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
	PostMessage(hwnd, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
	Sleep(irand(20, 50));
	PostMessage(hwnd, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); 
	PostMessage(hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(0, 0));
	PostMessage(hwnd, WM_MOUSELEAVE, 0, MAKELPARAM(0, 0));
}
